// Copyright 2024 - 2025 Crunchy Data Solutions, Inc.
//
// SPDX-License-Identifier: Apache-2.0

package collector

import (
	"context"
	"testing"

	"gotest.tools/v3/assert"

	"github.com/crunchydata/postgres-operator/internal/feature"
	"github.com/crunchydata/postgres-operator/internal/testing/require"
	"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
)

func TestEnablePatroniLogging(t *testing.T) {
	t.Run("EmptyInstrumentationSpec", func(t *testing.T) {
		gate := feature.NewGate()
		assert.NilError(t, gate.SetFromMap(map[string]bool{
			feature.OpenTelemetryLogs: true,
		}))
		ctx := feature.NewContext(context.Background(), gate)

		config := NewConfig(nil)
		cluster := new(v1beta1.PostgresCluster)
		require.UnmarshalInto(t, &cluster.Spec, `{
			instrumentation: {}
		}`)

		EnablePatroniLogging(ctx, cluster, config)

		result, err := config.ToYAML()
		assert.NilError(t, err)
		assert.DeepEqual(t, result, `# Generated by postgres-operator. DO NOT EDIT.
# Your changes will not be saved.
exporters:
  debug:
    verbosity: detailed
extensions:
  file_storage/patroni_logs:
    create_directory: true
    directory: /pgdata/patroni/log/receiver
    fsync: true
processors:
  batch/1s:
    timeout: 1s
  batch/200ms:
    timeout: 200ms
  batch/logs:
    send_batch_size: 8192
    timeout: 200ms
  groupbyattrs/compact: {}
  resource/patroni:
    attributes:
    - action: insert
      key: k8s.container.name
      value: database
    - action: insert
      key: k8s.namespace.name
      value: ${env:K8S_POD_NAMESPACE}
    - action: insert
      key: k8s.pod.name
      value: ${env:K8S_POD_NAME}
    - action: insert
      key: process.executable.name
      value: patroni
  resourcedetection:
    detectors: []
    override: false
    timeout: 30s
  transform/patroni_logs:
    log_statements:
    - statements:
      - set(instrumentation_scope.name, "patroni")
      - set(log.cache, ParseJSON(log.body["original"]))
      - set(log.severity_text, log.cache["levelname"])
      - set(log.severity_number, SEVERITY_NUMBER_DEBUG)  where log.severity_text ==
        "DEBUG"
      - set(log.severity_number, SEVERITY_NUMBER_INFO)   where log.severity_text ==
        "INFO"
      - set(log.severity_number, SEVERITY_NUMBER_WARN)   where log.severity_text ==
        "WARNING"
      - set(log.severity_number, SEVERITY_NUMBER_ERROR)  where log.severity_text ==
        "ERROR"
      - set(log.severity_number, SEVERITY_NUMBER_FATAL)  where log.severity_text ==
        "CRITICAL"
      - set(log.time, Time(log.cache["asctime"], "%F %T,%L")) where IsString(log.cache["asctime"])
      - set(log.attributes["log.record.original"], log.body["original"])
      - set(log.body, log.cache["message"])
receivers:
  filelog/patroni_jsonlog:
    include:
    - /pgdata/patroni/log/*.log
    - /pgdata/patroni/log/*.log.1
    operators:
    - from: body
      to: body.original
      type: move
    storage: file_storage/patroni_logs
service:
  extensions:
  - file_storage/patroni_logs
  pipelines:
    logs/patroni:
      exporters:
      - debug
      processors:
      - resource/patroni
      - transform/patroni_logs
      - resourcedetection
      - batch/logs
      - groupbyattrs/compact
      receivers:
      - filelog/patroni_jsonlog
`)
	})

	t.Run("InstrumentationSpecDefined", func(t *testing.T) {
		gate := feature.NewGate()
		assert.NilError(t, gate.SetFromMap(map[string]bool{
			feature.OpenTelemetryLogs: true,
		}))
		ctx := feature.NewContext(context.Background(), gate)

		cluster := new(v1beta1.PostgresCluster)
		cluster.Spec.Instrumentation = testInstrumentationSpec()
		config := NewConfig(cluster.Spec.Instrumentation)

		EnablePatroniLogging(ctx, cluster, config)

		result, err := config.ToYAML()
		assert.NilError(t, err)
		assert.DeepEqual(t, result, `# Generated by postgres-operator. DO NOT EDIT.
# Your changes will not be saved.
exporters:
  debug:
    verbosity: detailed
  googlecloud:
    log:
      default_log_name: opentelemetry.io/collector-exported-log
    project: google-project-name
extensions:
  file_storage/patroni_logs:
    create_directory: true
    directory: /pgdata/patroni/log/receiver
    fsync: true
processors:
  batch/1s:
    timeout: 1s
  batch/200ms:
    timeout: 200ms
  batch/logs:
    send_batch_size: 8192
    timeout: 200ms
  groupbyattrs/compact: {}
  resource/patroni:
    attributes:
    - action: insert
      key: k8s.container.name
      value: database
    - action: insert
      key: k8s.namespace.name
      value: ${env:K8S_POD_NAMESPACE}
    - action: insert
      key: k8s.pod.name
      value: ${env:K8S_POD_NAME}
    - action: insert
      key: process.executable.name
      value: patroni
  resourcedetection:
    detectors: []
    override: false
    timeout: 30s
  transform/patroni_logs:
    log_statements:
    - statements:
      - set(instrumentation_scope.name, "patroni")
      - set(log.cache, ParseJSON(log.body["original"]))
      - set(log.severity_text, log.cache["levelname"])
      - set(log.severity_number, SEVERITY_NUMBER_DEBUG)  where log.severity_text ==
        "DEBUG"
      - set(log.severity_number, SEVERITY_NUMBER_INFO)   where log.severity_text ==
        "INFO"
      - set(log.severity_number, SEVERITY_NUMBER_WARN)   where log.severity_text ==
        "WARNING"
      - set(log.severity_number, SEVERITY_NUMBER_ERROR)  where log.severity_text ==
        "ERROR"
      - set(log.severity_number, SEVERITY_NUMBER_FATAL)  where log.severity_text ==
        "CRITICAL"
      - set(log.time, Time(log.cache["asctime"], "%F %T,%L")) where IsString(log.cache["asctime"])
      - set(log.attributes["log.record.original"], log.body["original"])
      - set(log.body, log.cache["message"])
receivers:
  filelog/patroni_jsonlog:
    include:
    - /pgdata/patroni/log/*.log
    - /pgdata/patroni/log/*.log.1
    operators:
    - from: body
      to: body.original
      type: move
    storage: file_storage/patroni_logs
service:
  extensions:
  - file_storage/patroni_logs
  pipelines:
    logs/patroni:
      exporters:
      - googlecloud
      processors:
      - resource/patroni
      - transform/patroni_logs
      - resourcedetection
      - batch/logs
      - groupbyattrs/compact
      receivers:
      - filelog/patroni_jsonlog
`)
	})
}

func TestEnablePatroniMetrics(t *testing.T) {
	t.Run("EmptyInstrumentationSpec", func(t *testing.T) {
		gate := feature.NewGate()
		assert.NilError(t, gate.SetFromMap(map[string]bool{
			feature.OpenTelemetryMetrics: true,
		}))
		ctx := feature.NewContext(context.Background(), gate)

		config := NewConfig(nil)
		cluster := new(v1beta1.PostgresCluster)
		require.UnmarshalInto(t, &cluster.Spec, `{
			instrumentation: {}
		}`)

		EnablePatroniMetrics(ctx, cluster, config)

		result, err := config.ToYAML()
		assert.NilError(t, err)
		assert.DeepEqual(t, result, `# Generated by postgres-operator. DO NOT EDIT.
# Your changes will not be saved.
exporters:
  debug:
    verbosity: detailed
  prometheus/cpk-monitoring:
    endpoint: 0.0.0.0:9187
extensions: {}
processors:
  batch/1s:
    timeout: 1s
  batch/200ms:
    timeout: 200ms
  batch/logs:
    send_batch_size: 8192
    timeout: 200ms
  groupbyattrs/compact: {}
  resourcedetection:
    detectors: []
    override: false
    timeout: 30s
receivers:
  prometheus/cpk-monitoring:
    config:
      scrape_configs:
      - job_name: patroni
        scheme: https
        scrape_interval: 10s
        static_configs:
        - targets:
          - 0.0.0.0:8008
        tls_config:
          insecure_skip_verify: true
service:
  extensions: []
  pipelines:
    metrics/patroni:
      exporters:
      - prometheus/cpk-monitoring
      processors:
      - batch/200ms
      - groupbyattrs/compact
      receivers:
      - prometheus/cpk-monitoring
`)
	})

	t.Run("InstrumentationSpecDefined", func(t *testing.T) {
		gate := feature.NewGate()
		assert.NilError(t, gate.SetFromMap(map[string]bool{
			feature.OpenTelemetryMetrics: true,
		}))
		ctx := feature.NewContext(context.Background(), gate)

		cluster := new(v1beta1.PostgresCluster)
		cluster.Spec.Instrumentation = testInstrumentationSpec()
		config := NewConfig(cluster.Spec.Instrumentation)

		EnablePatroniMetrics(ctx, cluster, config)

		result, err := config.ToYAML()
		assert.NilError(t, err)
		assert.DeepEqual(t, result, `# Generated by postgres-operator. DO NOT EDIT.
# Your changes will not be saved.
exporters:
  debug:
    verbosity: detailed
  googlecloud:
    log:
      default_log_name: opentelemetry.io/collector-exported-log
    project: google-project-name
  prometheus/cpk-monitoring:
    endpoint: 0.0.0.0:9187
extensions: {}
processors:
  batch/1s:
    timeout: 1s
  batch/200ms:
    timeout: 200ms
  batch/logs:
    send_batch_size: 8192
    timeout: 200ms
  groupbyattrs/compact: {}
  resourcedetection:
    detectors: []
    override: false
    timeout: 30s
receivers:
  prometheus/cpk-monitoring:
    config:
      scrape_configs:
      - job_name: patroni
        scheme: https
        scrape_interval: 10s
        static_configs:
        - targets:
          - 0.0.0.0:8008
        tls_config:
          insecure_skip_verify: true
service:
  extensions: []
  pipelines:
    metrics/patroni:
      exporters:
      - prometheus/cpk-monitoring
      - googlecloud
      processors:
      - batch/200ms
      - groupbyattrs/compact
      receivers:
      - prometheus/cpk-monitoring
`)

	})
}
